package com.dhms.vostok1.model

//import android.content.pm.PackageManager
import android.annotation.SuppressLint
import android.content.Context
import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.dhms.basebusiness.R
import com.dhms.vostok1.data.*
import com.dhms.vostok1.network.MoshiObject
import com.dhms.vostok1.service.net.NetworkApi
import com.dhms.vostok1.utils.toAnyMap
import com.tencent.mmkv.MMKV
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch


class UserViewModel : ViewModel() {

    private val _isMessageLogin = MutableLiveData(false)
    val isMessageLogin: LiveData<Boolean> = _isMessageLogin

    private val _userInfo = MutableLiveData<LocalUserInfoData?>(null)
    val userInfo: LiveData<LocalUserInfoData?> = _userInfo

    private val _loginResult = MutableLiveData<ApiResult?>(null)
    val loginResult: LiveData<ApiResult?> = _loginResult

    private val _sendVerifyCodeResult = MutableLiveData<ApiResult?>(null)
    val sendVerifyCodeResult: LiveData<ApiResult?> = _sendVerifyCodeResult

    private val _sendVerifyCodeButtonDisabledTimeout = MutableLiveData(0)
    val sendVerifyCodeButtonDisableTimeout: LiveData<Int> = _sendVerifyCodeButtonDisabledTimeout

    private val _checkVerifyCodeResult = MutableLiveData<ApiResult?>(null)
    val checkVerifyCodeResult: LiveData<ApiResult?> = _checkVerifyCodeResult

    private val _resetPasswordResult = MutableLiveData<ApiResult?>(null)
    val resetPasswordResult: LiveData<ApiResult?> = _resetPasswordResult

    private val _getUserInfoResult = MutableLiveData<ApiResult?>(null)
    val getUserInfoResult: LiveData<ApiResult?> = _getUserInfoResult

    private val _getCustomerInfoResult = MutableLiveData<ApiResult?>(null)
    val getCustomerInfoResult: LiveData<ApiResult?> = _getCustomerInfoResult

//    private val _getAppVersionName = MutableLiveData<String?>(null)
//    val getAppVersionName: LiveData<String?> = _getAppVersionName

    @SuppressLint("StaticFieldLeak")
    private lateinit var context: Context
    private lateinit var sharedPref: MMKV

    fun setContextAndSharePref(context: Context, sharedPreferences: MMKV) {
        this.context = context
        sharedPref = sharedPreferences
    }

    fun switchLoginMethod() {
        _isMessageLogin.value = !isMessageLogin.value!!
    }

    private fun saveUserInfo(userInfo: LocalUserInfoData?) {
        Log.d(TAG, "Save user info to sharedpref: $userInfo")
        with(sharedPref.edit()) {
            putString(context.getString(R.string.user_phone), userInfo?.userPhone)
            putString(context.getString(R.string.password), userInfo?.password)
            putString(context.getString(R.string.token), userInfo?.token)
            putString(context.getString(R.string.avatar), userInfo?.avatar)
            putString(context.getString(R.string.username), userInfo?.username)
            putLong(context.getString(R.string.userId), userInfo?.userId ?: 0L)
            putLong(
                context.getString(R.string.organization_id),
                userInfo?.organizationId ?: 0L
            )
            putLong(context.getString(R.string.userId), userInfo?.userId ?: 0)
            putString(
                context.getString(R.string.origin_login_data),
                userInfo?.originLoginData
            )
            // TODO("数据量大的信息存在数据库中")
            putString(context.getString(R.string.origin_acl_data), userInfo?.originAclData)
            putString(context.getString(R.string.user_privileges), userInfo?.privileges)
            apply()
        }
    }

    fun getSavedUserInfo() {
        val userPhone = sharedPref.getString(context.getString(R.string.user_phone), null)
        val password = sharedPref.getString(context.getString(R.string.password), null)
        val token = sharedPref.getString(context.getString(R.string.token), null)
        val avatar = sharedPref.getString(context.getString(R.string.avatar), null)
        val username = sharedPref.getString(context.getString(R.string.username), null)
        val userId = sharedPref.getLong(context.getString(R.string.userId), 0L)
        val organizationId = sharedPref.getLong(context.getString(R.string.organization_id), 0)
        val originLoginData =
            sharedPref.getString(context.getString(R.string.origin_login_data), null)
        val originAclData = sharedPref.getString(context.getString(R.string.origin_acl_data), null)
        val privileges = sharedPref.getString(context.getString(R.string.user_privileges), null)
        if (!userPhone.isNullOrEmpty() && !password.isNullOrEmpty() && !token.isNullOrEmpty()) {
            _userInfo.value = LocalUserInfoData(
                userPhone,
                password,
                token,
                avatar,
                username,
                organizationId = organizationId,
                userId = userId,
                originLoginData = originLoginData,
                originAclData = originAclData,
                privileges = privileges
            )
            NetworkApi.token = token
            NetworkApi.organizationId = organizationId
            NetworkApi.userId = userId
            Log.d(TAG, "Got user info from sharedpref: ${userInfo.value}")
        } else {
            Log.e(
                TAG,
                "Failed to get user info from sharedpref or the info is null."
            )
            _userInfo.value = null
        }
    }

    fun login(userPhone: String, password: String) {
        if (loginResult.value?.state == ApiState.LOADING) {
            return
        }
        _loginResult.value = ApiResult(ApiState.LOADING)
        viewModelScope.launch {
            try {
                val loginRes = NetworkApi.login(
                    LoginReqBody(
                        userPhone,
                        password,
                        USER_PLATFORM,
                        USER_PROJECT_NAME,
                        USER_PRVSET_NAME
                    )
                )
                Log.d(TAG, "res: $loginRes")

                val token = loginRes.headers()["X-Auth-Tkn"]
                val resBody = loginRes.body()

                if (token?.isNotEmpty() == true) {
                    val organizationInfo = getOrganizationInfo(resBody)
                    val privileges = getPrivileges(resBody)

                    val userInfo = LocalUserInfoData(
                        userPhone,
                        password,
                        token,
                        organizationId = organizationInfo.organizationId,
                        userId = resBody?.data?.userId,
                        originLoginData = MoshiObject.jsonAdapter.toJson(resBody?.data.toAnyMap()),
                        originAclData = organizationInfo.organizationInfo,
                        privileges = privileges
                    )
                    _userInfo.value = userInfo
                    NetworkApi.token = token
                    NetworkApi.organizationId = organizationInfo.organizationId
                    NetworkApi.userId = userInfo.userId
                    saveUserInfo(userInfo)
                    _loginResult.value = ApiResult(ApiState.DONE)
                } else {
                    val e = resBody?.message ?: loginRes.message()
                    Log.e(TAG, "error: $e")
                    _loginResult.value = ApiResult(ApiState.ERROR, e ?: "登录失败")
                }
            } catch (e: Exception) {
                Log.e(TAG, "error: $e")
                _loginResult.value = ApiResult(ApiState.ERROR, e.message ?: "登录失败")
            }
        }
    }

    fun messageLogin(phone: String, code: String) {
        if (loginResult.value?.state == ApiState.LOADING) {
            return
        }
        _loginResult.value = ApiResult(ApiState.LOADING)
        viewModelScope.launch {
            try {
                val loginRes = NetworkApi.messageLogin(MessageLoginReqBody(phone, code))
                Log.d(TAG, "res: $loginRes")

                val token = loginRes.headers()["X-Auth-Tkn"]
                val resBody = loginRes.body()

                if (token?.isNotEmpty() == true) {
                    val organizationInfo = getOrganizationInfo(resBody)
                    val privileges = getPrivileges(resBody)
                    val userInfo = LocalUserInfoData(
                        phone,
                        "unknown",
                        token,
                        organizationId = organizationInfo.organizationId,
                        userId = loginRes.body()?.data?.userId,
                        originLoginData = MoshiObject.jsonAdapter.toJson(resBody?.data.toAnyMap()),
                        originAclData = organizationInfo.organizationInfo,
                        privileges = privileges
                    )
                    _userInfo.value = userInfo
                    NetworkApi.token = token
                    NetworkApi.organizationId = organizationInfo.organizationId
                    NetworkApi.userId = userInfo.userId
                    saveUserInfo(userInfo)
                    _loginResult.value = ApiResult(ApiState.DONE)
                } else {
                    val e = loginRes.body()?.message ?: loginRes.message()
                    Log.e(TAG, "error: $e")
                    _loginResult.value = ApiResult(ApiState.ERROR, e ?: "登录失败")
                }
            } catch (e: Exception) {
                Log.e(TAG, "error: $e")
                _loginResult.value = ApiResult(ApiState.ERROR, e.message ?: "登录失败")
            }
        }
    }

    @Suppress("UNCHECKED_CAST")
    private fun getOrganizationInfo(resBody: LoginResData?): UserOrganizationInfo {
        val aclSvcString = resBody?.refData?.get("acl_svc") as String?
        var organizationInfo: Map<String, Any>? = null
        var organizationId: Double? = null
        if (aclSvcString != null) {
            organizationInfo =
                MoshiObject.jsonAdapter.fromJson(aclSvcString)?.get("data") as Map<String, Any>
            organizationId = organizationInfo["my_org_id"] as Double?
        }
        return UserOrganizationInfo(
            organizationId?.toLong(),
            MoshiObject.jsonAdapter.toJson(organizationInfo)
        )
    }

    @Suppress("UNCHECKED_CAST")
    private fun getPrivileges(resBody: LoginResData?): String? {
        val aclSvcString = resBody?.refData?.get("acl_svc") as String?
        var result: String? = null
        aclSvcString?.let {
            (MoshiObject.jsonAdapter.fromJson(aclSvcString)
                ?.get("data") as Map<String, Any>?)?.let { data ->
                (data.get(key = "role_map") as Map<String, Map<String, Any>>?)?.let { roleMap ->
                    (roleMap.values.map { it.get(key = "privileges") } as List<Map<String, Any>>?)?.let { privileges ->
                        result = privileges.map { it.keys }.joinToString(",")
                    }
                }
            }
        }
        return result
    }

    fun logout() {
        NetworkApi.token = null
        NetworkApi.organizationId = null
        NetworkApi.userId = null
        _userInfo.postValue(null)
        saveUserInfo(null)
    }

    fun getUserInformation() {
        if (userInfo.value == null) {
            return
        }
        viewModelScope.launch {
            val res = NetworkApi.getUserInfo()
            Log.d(TAG, "res: $res")

            val e = res.exceptionOrNull()
            if (e != null) {
                Log.e(TAG, "error: $e")
                _getUserInfoResult.value = ApiResult(ApiState.ERROR, e.message)
                return@launch
            }

            val savedUserInfo = userInfo.value!!.copy()
            val infoData = res.getOrNull()?.body()
            savedUserInfo.userId = infoData?.data?.usId
            savedUserInfo.avatar = infoData?.data?.usAvatar
            savedUserInfo.username = infoData?.data?.usName
            _userInfo.value = savedUserInfo
            saveUserInfo(savedUserInfo)
            _getUserInfoResult.value = ApiResult(ApiState.DONE, null)
        }
    }

    fun getCustomerInfo() {
        viewModelScope.launch {
            val res = NetworkApi.getCustomerInfo()
            Log.d(TAG, "res: $res")

            val e = res.exceptionOrNull()
            if (e != null) {
                Log.e(TAG, "error: $e")
                _getCustomerInfoResult.value = ApiResult(ApiState.ERROR, e.message)
                return@launch
            }

            val savedUserInfo = userInfo.value!!.copy()
            val infoData = res.getOrNull()?.body()
            savedUserInfo.cuName = infoData?.data?.cuName
            _userInfo.value = savedUserInfo
            saveUserInfo(savedUserInfo)
            _getUserInfoResult.value = ApiResult(ApiState.DONE, null)
        }
    }

    fun sendVerifyCode(phone: String) {
        viewModelScope.launch {
            val res = NetworkApi.sendVerifyCode(VerifyCodeReqBody(phone))
            Log.d(TAG, "Send verify code res: $res")

            val e = res.exceptionOrNull()
            if (e != null) {
                Log.e(TAG, "Send verify code error: $e")
                _sendVerifyCodeResult.value = ApiResult(ApiState.ERROR, e.message)
                return@launch
            }
            _sendVerifyCodeResult.value = ApiResult(ApiState.DONE, null)
            startDisableSendVerifyCodeButtonCountDown()
        }
    }

    private fun startDisableSendVerifyCodeButtonCountDown() {
        viewModelScope.launch {
            repeat(60) { i ->
                _sendVerifyCodeButtonDisabledTimeout.value = 60 - 1 - i
                delay(1000)
            }
        }
    }

    private suspend fun checkVerifyCode(phone: String, code: String): Boolean {
        val res = NetworkApi.checkVerifyCode(CheckVerifyCodeReqBody(phone, code))
        Log.d(TAG, "Check verify code res: $res")
        val e = res.exceptionOrNull()
        if (e != null) {
            Log.e(TAG, "Check verify code error: $e")
            return false
        }
        return true
    }

    fun resetPassword(phone: String, code: String, password: String) {
        viewModelScope.launch {
            if (!checkVerifyCode(phone, code)) {
                Log.e(TAG, "The verification code is wrong.")
                _resetPasswordResult.value = ApiResult(ApiState.ERROR, "验证码错误！")
                return@launch
            }
            val res = NetworkApi.resetPassword(ResetPasswordReqBody(phone, code, password))
            Log.d(TAG, "Reset password res: $res")
            val e = res.exceptionOrNull()
            if (e != null) {
                Log.e(TAG, "Reset password error: $e")
                _resetPasswordResult.value = ApiResult(ApiState.ERROR, e.message)
                return@launch
            }
            _resetPasswordResult.value = ApiResult(ApiState.DONE, null)
        }
    }

    fun clearApiResults() {
        _loginResult.value = null
        _resetPasswordResult.value = null
        _sendVerifyCodeResult.value = null
        _checkVerifyCodeResult.value = null
        _getUserInfoResult.value = null
    }

    companion object {
        private const val TAG = "UserInfoViewModel"
        private const val USER_PLATFORM = "APP"
        private const val USER_PROJECT_NAME = "DHMS"
        private const val USER_PRVSET_NAME = "user"
    }
}